package lac.serviceflow;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.thoughtworks.xstream.XStream;

import lac.model.core.persistent.Session;
import lac.model.core.persistent.criterion.Criteria;
import lac.model.core.persistent.criterion.Order;
import lac.serviceflow.cfg.FlowMethod;
import lac.serviceflow.cfg.FlowMethodParam;
import lac.serviceflow.cfg.FlowParam;
import lac.serviceflow.cfg.ServiceFlow;
import lac.serviceflow.cfg.model.ServiceFlowModelDescribe;
import lac.serviceuint.MethodExecuteMessage;
import lac.serviceuint.ServiceUnitExecuter;
import lac.serviceuint.tool.StringToTypeInstantiation;

public class ServiceFlowEnginer {

	
	private static Logger log = LoggerFactory.getLogger(ServiceFlowEnginer.class);
	
	@Autowired(required=false)
	private ServiceUnitExecuter serviceUnitExecuter;
	
	private Map<String,ServiceFlow> flowContext = new LinkedHashMap<String,ServiceFlow>();
	
	private ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("nashorn");
	
	private List<FlowInterceptor> flowInterceptors = new ArrayList<FlowInterceptor>();
	
	private static XStream xstream = new XStream();
	static{
		xstream.processAnnotations(ServiceFlow.class);
		xstream.setMode(XStream.NO_REFERENCES);
	}
	
	public Object doFlow(String flowName,Object... params) throws Exception{
		ServiceFlow serviceFlow = flowContext.get(flowName);
		if(serviceFlow != null){
			long  startTime = System.currentTimeMillis();
			log.info("   |_ServiceFlow:"+flowName+"开始执行。");
			MethodExecuteMessage message = null;
			Map<String,Object> flowParamMap = createFlowParamMap(serviceFlow.getFlowParams(),params);
			doFlowInterceptors(true,flowInterceptors,serviceFlow,flowParamMap);
			List<FlowMethod> flowMethods = serviceFlow.getFlowMethods();
			for(FlowMethod flowMethod : flowMethods){
				Map<Integer,String> globalParamIndexName = new HashMap<Integer,String>();
				String serviceUnitName = flowMethod.getServiceUnitName();
				String methodName = flowMethod.getMethodName();
				List<FlowMethodParam> flowMethodParams = flowMethod.getFlowMethodParams();
				List<Object> paramList = createMethodParamList(flowMethodParams,flowParamMap,globalParamIndexName,message);
				if(validateCondition(flowMethod.getCondition(),flowParamMap)){
					message = serviceUnitExecuter.doExecute(serviceUnitName, methodName, paramList.toArray());
					resetFlowParamMap(flowParamMap,message.getParams(),globalParamIndexName);
				}
			}
			long endTime = System.currentTimeMillis();
			doFlowInterceptors(false,flowInterceptors,serviceFlow,flowParamMap);
			log.info("   <-ServiceFlow:"+flowName+"执行结束。"+"执行时间："+String.valueOf(endTime-startTime)+"毫秒");
			if(message == null){
				return null;
			}else{
				return message.getReturnValue();
			}
		}else{
			throw new RuntimeException("ServiceFlow:"+flowName+"没有被加载！");
		}
	}
	
	private List<Object> createMethodParamList(List<FlowMethodParam> flowMethodParams,Map<String,Object> flowParamMap,Map<Integer,String> globalParamIndexName,MethodExecuteMessage message){
		List<Object> paramList = new ArrayList<Object>();
		for(FlowMethodParam flowMethodParam : flowMethodParams){
			boolean isGlobalParam = false;
			Object value = null;
			Integer index = flowMethodParam.getIndex();
			String globalName = flowMethodParam.getGlobalName();
			String localName = flowMethodParam.getLocalName();
			if(globalName != null){
				value = flowParamMap.get(globalName);
				isGlobalParam =true;
			}
			if(localName != null && message != null){
				Map<Object,Object> internalVar = message.getInternalVar();
				value = internalVar.get(localName);
				isGlobalParam =false;
			}
			while(paramList.size() < index+1){
				paramList.add(null);
			}
			if(isGlobalParam){
				globalParamIndexName.put(index,globalName);
			}
			paramList.set(index, value);
		}
		return paramList;
	}
	
	private void resetFlowParamMap(Map<String,Object> flowParamMap,Object[] params,Map<Integer,String> globalParamIndexName){
		for(int i=0;i<params.length;i++){
			String globalParamName = globalParamIndexName.get(i);
			if(globalParamName != null){
				flowParamMap.put(globalParamName, params[i]);
			}
			
		}
	}

	private Map<String,Object>  createFlowParamMap(List<FlowParam> flowParams,Object[] params) throws Exception{
		Map<String,Object> flowParamMap = new HashMap<String,Object>();
		for(FlowParam flowParam : flowParams){
			String defalutValue = flowParam.getDefalutValue();
			String paramType = flowParam.getJavaClassType();
			Integer index = flowParam.getIndex();
			Object value = null;
			if(index<params.length){
				value = params[index];
			}
			if(defalutValue!=null && value==null){
				value = StringToTypeInstantiation.instance(paramType, defalutValue);
			}
			flowParamMap.put(flowParam.getName(), value);
		}
		return flowParamMap;
	}
	
	private Boolean validateCondition(String condition,Map<String,Object> flowParamMap) throws ScriptException{
		if(condition == null){
			return true;
		}else{
			SimpleBindings bindings = new SimpleBindings();
			bindings.putAll(flowParamMap);
			return Boolean.TRUE.equals(scriptEngine.eval(condition, bindings));
		}
	}
	
	public void init(){
		if(serviceUnitExecuter.getSessionFactory() != null){
			Session session = serviceUnitExecuter.getSessionFactory().getSession();
			Criteria criteria = new Criteria();
			criteria.addOrder(new Order("flowMethods.sort", 1));
			List<ServiceFlow> serviceFlows = session.query(new ServiceFlowModelDescribe().getName(),criteria,ServiceFlow.class);
			for(ServiceFlow serviceFlow : serviceFlows){
				flowContext.put(serviceFlow.getName(), serviceFlow);
			}
		}
	}
	
	public void addServiceFlow(InputStream inputStream){
		ServiceFlow serviceFlow= (ServiceFlow) xstream.fromXML(inputStream);
		addServiceFlow(serviceFlow);
	}
	
	public void addServiceFlow(ServiceFlow serviceFlow){
		log.info("加载ServiceFlow："+serviceFlow.getName());
		flowContext.put(serviceFlow.getName(), serviceFlow);
	}
	
	private void doFlowInterceptors(boolean isPre,List<FlowInterceptor> flowInterceptors,ServiceFlow serviceFlow,Map<String,Object> flowParamMap){
		if(flowInterceptors != null){
			for(FlowInterceptor interceptor : flowInterceptors){
				if(isPre){
					interceptor.preLogicHandle(serviceFlow,flowParamMap);
				}else{
					interceptor.postLogicHandle(serviceFlow,flowParamMap);
				}
				
			}
		}
	}

	public ServiceUnitExecuter getServiceUnitExecuter() {
		return serviceUnitExecuter;
	}

	public void setServiceUnitExecuter(ServiceUnitExecuter serviceUnitExecuter) {
		this.serviceUnitExecuter = serviceUnitExecuter;
	}

	public Map<String, ServiceFlow> getFlowContext() {
		return flowContext;
	}

	public void setFlowContext(Map<String, ServiceFlow> flowContext) {
		this.flowContext = flowContext;
	}

	public List<FlowInterceptor> getFlowInterceptors() {
		return flowInterceptors;
	}

	public void setFlowInterceptors(List<FlowInterceptor> flowInterceptors) {
		this.flowInterceptors = flowInterceptors;
	}
	
	
}
